package se.liu.oschi129.filenameparser;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;

/**
 * This class can be use to divide a string into a sequence of filenames from given directory.
 * The parsing supports three regular-expressions which can be used to define filenames.
 * 
 * @author oschi129
 * 
 * ============================================================================
 * ========================== Usage information ===============================
 * ============================================================================
 * 
 * This class supports the following regular-expressions:
 * 
 * 1. Separator "|"
 * 
 * 		The separator can be used to separate indexes or filenames.
 * 		- Example 1:
 * 
 * 			"/home/user/docs/image.png|picture.png"
 * 
 * 			Will return:
 * 			{ "/home/user/docs/image.png", "/home/user/docs/picture.png" }
 * 
 * 		- Example 2:
 * 
 * 			"/home/user/docs/image01|02"
 * 
 * 			Will return:
 * 			{ "/home/user/docs/image01", "/home/user/docs/image02" }
 * 
 * 2. Range ">"
 * 
 * 		The range can be used to load a range of indexes.
 * 		- Example 1:
 * 
 * 			"/home/user/docs/image01>03"
 * 
 * 			Will return:
 * 			{ "/home/user/docs/image01", "/home/user/docs/image02" , "/home/user/docs/image03"}
 * 
 * 			Note: The range be defined in increasing as well as decreasing order.
 * 
 * 3. Extension ":"
 * 
 * 		The extension regular-expression can be used to set a extension to a pattern.
 * 		- Example 1:
 * 
 * 			"/root/img01>03|02:.png"
 *
 * 			Will return:
 * 			{ "/root/img01.png", "/root/img02.png", "/root/img03.png", "/root/img02.png"}
 *
 */
public class FilenameParser {

	private LinkedList<String> content = new LinkedList<String>();
	
	/** Creates a new FilenameParser with a given regular-expression */
	public FilenameParser(String regularExpression) {
		String[] pathArray = regularExpression.split("/|\\\\");
		StringBuilder pathBuilder = new StringBuilder();
		for (int n = 0; n < pathArray.length-1 ; n++)
			pathBuilder.append(pathArray[n] + "/");
		LinkedList<String> basenames = parseExpression(pathArray[pathArray.length-1]);
		String path = pathBuilder.toString();
		for (String basename : basenames)
			content.add(path + basename);
	}
	
	/**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - - Public methods - - - - - - - - - - 
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	public Collection<String> getFilenames() {
		return Collections.unmodifiableList(content);
	}
	
	/**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - Private methods - - - - - - - - - - 
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	/** Returns a linked list of basenames parsed from an expression */
	private LinkedList<String> parseExpression(String expression) {
		String[] operationArray = expression.split("[^>:|]");
		String[] contentArray = expression.split("[>:|]");
		LinkedList<String> operationList = new LinkedList<String>();
		LinkedList<String> contentList = new LinkedList<String>();
		for (int n = 0; n < operationArray.length; n++)
			if (operationArray[n].length() > 0)
				operationList.add(operationArray[n]);
		for (int n = 0; n < contentArray.length; n++)
			contentList.add(contentArray[n]);
		
		if (contentList.size() - operationList.size() == 1)
			return buildBasenames(contentList,operationList);
		else
			return null;
		
	}
	
	/** Builds up a linked list of basenames from an operation list {|,>,:} and a content list (strings) */
	private LinkedList<String> buildBasenames(LinkedList<String> content, LinkedList<String> operations) {
		String basename = "", tempBasename, lastIndex = "", index, operation = "|";
		LinkedList<String> extensionlessResult = new LinkedList<String>();
		LinkedList<String> result = new LinkedList<String>();
		
		while (true) {
			if (content.isEmpty()) {
				result.addAll(extensionlessResult);
				return result;
			}
			
			tempBasename = getBasenameName(content.getFirst());
			if (tempBasename.length() > 0)
				basename = tempBasename;
			index = getBasenameIndex(content.getFirst());
			content.removeFirst();
			
			
			
			if (operation.equals(">")) {
				extensionlessResult.addAll(createRange(basename,lastIndex,index));
			} else if (operation.equals("|")) {
					extensionlessResult.add(basename + index);
			} else if (operation.equals(":")) {
				for(String str : extensionlessResult)
					result.add(str + basename + index);
				extensionlessResult.clear();
			}
			
			lastIndex = index;
			if (operations.isEmpty()) {
				result.addAll(extensionlessResult);
				return result;
			}
			operation = operations.getFirst();
			operations.removeFirst();
			
		}
	}
	
	/** Returns a range of basenames from firstIndex to lastIndex.
	 * Example: createRange("image", "01", "03") --> ( "image01", "image02", "image03") */
	private LinkedList<String> createRange(String basename, String firstIndex, String lastIndex) {
		int first = new Integer(firstIndex);
		int last = new Integer(lastIndex);
		int length = Math.max(firstIndex.length(), lastIndex.length());
		LinkedList<String> range = new LinkedList<String>();
		if (first < last)
			for (int n = first+1; n <= last; n++)
				range.add(basename + String.format("%0"+length+"d", n));
		else if (first > last)
			for (int n = first-1; n >= last; n--)
				range.add(basename + String.format("%0"+length+"d", n));
		return range;
	}
	
	/** Returns the index of a basename. 
	 * Example getIndex("picture02") -> "02" */
	private String getBasenameIndex(String str) {
		StringBuilder basename = new StringBuilder();
		char character;
		for (int n = str.length()-1; n >= 0; n--) {
			character = str.charAt(n);
			if (Character.isDigit(character))
				basename.insert(0, character);
			else
				return basename.toString();
		}
		return basename.toString();
	}
	
	/** Returns the name of a basename (without index). 
	 * Example getIndex("picture02") -> "picture" */
	private String getBasenameName(String str) {
		StringBuilder basename = new StringBuilder();
		char character;
		boolean start = false;
		for (int n = str.length()-1; n >= 0; n--) {
			character = str.charAt(n);
			if (!Character.isDigit(character)) {
				start = true;
				basename.insert(0, character);
			}
			else if (start)
				basename.insert(0, character);
		}
		return basename.toString();
	}
	
}
